////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2010-2011 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// LCD Driver for WX-MP3PLCD-F-001 board (http://www.ejigu.com/ca28/57/p-r-s/)
//      TS8026Y (http://www.aitendo.com/product/3866)
//      LCD controler: ST7781
//      ST7781.c (http://homepage3.nifty.com/fpga/files/board/WX-MP3PLCD-F-001/asp_mp3p_lcd_fm3_20120610.zip)
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "ST7781.h"

void P8_Initialize(void);
void LCD_nRESET(int n);
void P8_LCD_cmd(volatile unsigned short index);
void P8_LCD_dat(volatile unsigned short data);
unsigned short P8_LCD_read_dat(volatile unsigned short index);

ST7781_Driver g_ST7781_Driver;

static void delayms(volatile UINT32 n)
{
    HAL_Time_Sleep_MicroSeconds_InterruptEnabled(n * 1000);
}

BOOL ST7781_Driver::Initialize()
{
    unsigned short id;
    P8_Initialize();

    LCD_nRESET(0);
    delayms(30);        // Delay 30ms // This delay time is necessary
    LCD_nRESET(1);
    delayms(30);        // Delay 30ms // This delay time is necessary

    id = P8_LCD_read_dat(0x0000);

    P8_LCD_cmd(0x00ff);P8_LCD_dat(0x0001);
    P8_LCD_cmd(0x00f3);P8_LCD_dat(0x0008);
    //LCD_CtrlRead_ST7781 (0x00f3);         //Read Parameter
    P8_LCD_cmd(0x0001);P8_LCD_dat(0x0100);  //Output Direct
    P8_LCD_cmd(0x0002);P8_LCD_dat(0x0700);  //Line Inversion
    P8_LCD_cmd(0x0003);P8_LCD_dat(0x1030);  //Entry Mode (65K, BGR)
    P8_LCD_cmd(0x0008);P8_LCD_dat(0x0807);  //Porch Setting
    P8_LCD_cmd(0x0009);P8_LCD_dat(0x0000);  //Scan Cycle
    P8_LCD_cmd(0x000A);P8_LCD_dat(0x0000);  //FMARK off
    P8_LCD_cmd(0x0010);P8_LCD_dat(0x0790);  //Power Control1
    P8_LCD_cmd(0x0011);P8_LCD_dat(0x0005);  //Power Control2
    P8_LCD_cmd(0x0012);P8_LCD_dat(0x0000);  //Power Control3
    P8_LCD_cmd(0x0013);P8_LCD_dat(0x0000);  //Power Control4
    delayms(100);;                          //Delay 100ms
    P8_LCD_cmd(0x0010);P8_LCD_dat(0x1490);  //Power Control1
    delayms(50);                            //Delay 50ms
    P8_LCD_cmd(0x0011);P8_LCD_dat(0x0227);  //Power Control2
    delayms(50);                            //Delay 50ms
    P8_LCD_cmd(0x0012);P8_LCD_dat(0x0089);  //Power Control3
    P8_LCD_cmd(0x0013);P8_LCD_dat(0x1900);  //Power Control4
    P8_LCD_cmd(0x0029);P8_LCD_dat(0x0021);  //VCOMH setting
    delayms(50);                            //Delay 50ms
    P8_LCD_cmd(0x0030);P8_LCD_dat(0x0000);
    P8_LCD_cmd(0x0031);P8_LCD_dat(0x0006);
    P8_LCD_cmd(0x0032);P8_LCD_dat(0x0100);
    P8_LCD_cmd(0x0035);P8_LCD_dat(0x0001);
    P8_LCD_cmd(0x0036);P8_LCD_dat(0x0000);
    P8_LCD_cmd(0x0037);P8_LCD_dat(0x0000);
    P8_LCD_cmd(0x0038);P8_LCD_dat(0x0406);
    P8_LCD_cmd(0x0039);P8_LCD_dat(0x0202);
    P8_LCD_cmd(0x003c);P8_LCD_dat(0x0001);
    P8_LCD_cmd(0x003d);P8_LCD_dat(0x0000);
    P8_LCD_cmd(0x0050);P8_LCD_dat(0x0000);  //Horizontal Address Start Position
    P8_LCD_cmd(0x0051);P8_LCD_dat(0x00ef);  //Horizontal Address End Position
    P8_LCD_cmd(0x0052);P8_LCD_dat(0x0000);  //Vertical Address Start Position
    P8_LCD_cmd(0x0053);P8_LCD_dat(0x013f);  //Vertical Address End Position
    P8_LCD_cmd(0x0060);P8_LCD_dat(0xa700);  //Gate scan control
    P8_LCD_cmd(0x0061);P8_LCD_dat(0x0001);  //Non-display Area setting
    P8_LCD_cmd(0x0090);P8_LCD_dat(0x0033);  //RTNI setting
    P8_LCD_cmd(0x0007);P8_LCD_dat(0x0133);  //Display Control1
    delayms(50);                            //Delay 50ms
    P8_LCD_cmd(0x0022);

#ifdef LCD_SPLASH
    P8_LCD_cmd(0x0022);
    unsigned int i,n,m;
    unsigned short color[8]={0xf800,0x07e0,0x001f,0x07ff,0xf81f,0xffe0,0x0000,0xffff};
    for(n=0;n<8;n++)
        for(i=0;i<40;i++)
            for(m=0;m<240;m++)
                P8_LCD_dat(0xffff);

    for(n=0;n<8;n++)
        for(i=0;i<40;i++)
            for(m=0;m<240;m++)
                P8_LCD_dat(color[n]);
#endif
}

BOOL ST7781_Driver::Uninitialize()
{
}

void ST7781_Driver::PowerSave(BOOL On)
{
}

void ST7781_Driver::Clear()
{
    UINT32 x;
    g_ST7781_Driver.m_cx = 0;
    g_ST7781_Driver.m_cy = 0;
    P8_LCD_cmd(0x0020);
    P8_LCD_dat(0);
    P8_LCD_cmd(0x0021);
    P8_LCD_dat(0);
    P8_LCD_cmd(0x0022);
    for (x = 0; x < (g_ST7781_Config.Width * g_ST7781_Config.Height); x++){
        P8_LCD_dat(0x0000);
    }
}

void ST7781_Driver::BitBltEx565(int x, int y, int width, int height, UINT32 data[])
{
    int i, j;
    UINT16 v1, v2;
    UINT16 *pdata = (UINT16 *)data;
    for (j = 0; j < height; j ++) {
        P8_LCD_cmd(0x0020);
        P8_LCD_dat(x);
        P8_LCD_cmd(0x0021);
        P8_LCD_dat(y + j);
        P8_LCD_cmd(0x0022);
        for (i = 0; i < width; i ++) {
            P8_LCD_dat(*(pdata + i));
        }
        pdata += g_ST7781_Config.Width;
    }
}

void ST7781_Driver::BitBltEx( int x, int y, int width, int height, UINT32 data[] )
{
    int i, j;
    UINT16 v1, v2;
    UINT16 *pdata = (UINT16 *)data;
    pdata += (y * g_ST7781_Config.Width + x);
    ST7781_Driver::BitBltEx565(x, y, width, height, (UINT32 *)pdata);
}

void ST7781_Driver::WriteChar_Color(unsigned char c, UINT8 cx, UINT8 cy, UINT16 fgcol, UINT16 bgcol)
{
    UINT8 x, y;
    UINT16 col;
    UINT16 sx = cx * Font_Width() + g_ST7781_Config.TextSx;
    UINT16 sy = cy * Font_Height() + g_ST7781_Config.TextSy;

    unsigned char *font = (unsigned char *)Font_GetGlyph(c);
    for (y = 0; y < Font_Height(); y++) {
        P8_LCD_cmd(0x0020);
        P8_LCD_dat(sx);
        P8_LCD_cmd(0x0021);
        P8_LCD_dat(sy + y);
        P8_LCD_cmd(0x0022);
        for (x = 0; x < Font_Width(); x++) {
            if (font[y] & (0x80 >> x)) {
                col = fgcol;
            } else {
                col = bgcol;
            }
            P8_LCD_dat(col);
        }
    }
}

void ST7781_Driver::WriteChar(unsigned char c, int cx, int cy)
{
    WriteChar_Color(c, (UINT8)cx, (UINT8)cy, g_ST7781_Config.fcol, g_ST7781_Config.bcol);
}

void ST7781_Driver::WriteFormattedChar(unsigned char ch)
{
    UINT16 colums = g_ST7781_Config.Width/Font_Width();
    UINT16 rows = g_ST7781_Config.Height/Font_Height();
    if (ch == 0xc) {
        Clear();
        g_ST7781_Driver.m_cx = 0;
        g_ST7781_Driver.m_cy = 0;
    } else if (ch == '\n') {
        g_ST7781_Driver.m_cy++;
        if (g_ST7781_Driver.m_cy == rows) {
            g_ST7781_Driver.m_cy = 0;
        }
    } else if (ch == '\r') {
        g_ST7781_Driver.m_cx = 0;
    } else {
        WriteChar(ch, g_ST7781_Driver.m_cx, g_ST7781_Driver.m_cy);
        g_ST7781_Driver.m_cx ++;
        if (g_ST7781_Driver.m_cx == colums) {
            g_ST7781_Driver.m_cx = 0;
            g_ST7781_Driver.m_cy ++;
            if (g_ST7781_Driver.m_cy == rows) {
                g_ST7781_Driver.m_cy = 0;
            }
        }
    }
}
